﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections;
using System.Reflection;
using System.Linq.Expressions;

namespace LinqToPowershell
{    
    /// <summary>
    /// This wrapper serves two purposes:
    /// (1) since it composes the IEnumerable instead of inheriting from it, it stops Powershell's eager evaluation
    /// (2) its contructors allow Powershell to coerce any collection into an IQueryable
    /// </summary>    
    public class QueryableWrapper<T> : QueryableWrapper, IQueryableWrapper<T>, IQueryableWrapper
    {
        public QueryableWrapper(Expression expression, IQueryProvider provider, Type elementType, Func<IEnumerator<T>> getEnumerator)
            : base(expression, provider, elementType, () => getEnumerator())
        {
            this.GetEnumeratorFunc = getEnumerator;
        }

        public QueryableWrapper(IQueryableWrapper<T> copy)
            : base(copy)
        {
            this.GetEnumeratorFunc = copy.GetEnumeratorFunc;
        }
        
        public QueryableWrapper(IQueryable<T> queryable)
            : this(queryable.Expression, queryable.Provider, queryable.ElementType, queryable.GetEnumerator)
        { }
        
        public QueryableWrapper(IEnumerable<T> enumerable)
            : this(enumerable.AsQueryable())
        { }        

        public new IQueryable<T> Query
        {
            get
            {
                return new EagerQueryable<T>(this);
            }
        }

        public new Func<IEnumerator<T>> GetEnumeratorFunc { get; protected set; }
    }

    /// <summary>
    /// This wrapper serves two purposes:
    /// (1) since it composes the IEnumerable instead of inheriting from it, it stops Powershell's eager evaluation
    /// (2) its contructors allow Powershell to coerce any collection into an IQueryable
    /// </summary> 
    public class QueryableWrapper : IQueryableWrapper
    {
        public QueryableWrapper(Expression expression, IQueryProvider provider, Type elementType, Func<IEnumerator> getEnumerator)
        {
            Expression = expression;
            Provider = provider;
            ElementType = elementType;
            GetEnumeratorFunc = getEnumerator;
        }

        public QueryableWrapper(IQueryableWrapper copy)
            : this(copy.Expression, copy.Provider, copy.ElementType, copy.GetEnumeratorFunc)
        { }

        public QueryableWrapper(IQueryable queryable)
            : this(queryable.Expression, queryable.Provider, queryable.ElementType, queryable.GetEnumerator)
        { }

        public QueryableWrapper(IEnumerable enumerable)
            : this(enumerable.AsQueryable())
        { }

        public IQueryable Query
        {
            get { return new EagerQueryable(this); }
        }

        public Expression Expression { get; protected set; }
        public IQueryProvider Provider { get; protected set; }
        public Type ElementType { get; protected set; }
        public Func<IEnumerator> GetEnumeratorFunc { get; protected set; }
    }   
}
